/**
 * Copyright (C) 2010 David Simonek <dafesimonek@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.dafe.partyband.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.text.DateFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;
import org.dafe.partyband.db.api.IAuthor;
import org.dafe.partyband.db.api.IConcert;
import org.dafe.partyband.db.api.ISong;
import org.dafe.partyband.db.api.ISongResource;
import org.dafe.partyband.gui.songs.SongsTable;
import org.dafe.partyband.importer.api.IImporterServices;
import org.dafe.partyband.importer.api.IMimeTypeDetector;
import org.dafe.partyband.importer.api.INameEncoder;
import org.dafe.partyband.importer.api.ISongImport.IImportIssue;
import org.dafe.partyband.services.api.Go2URIAction;
import org.dafe.partyband.services.api.IMusicService;
import org.dafe.partyband.services.api.IURIActionDescription;
import org.dafe.partyband.services.api.MusicServiceRegistry;
import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXMultiSplitPane;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.MultiSplitLayout.Divider;
import org.jdesktop.swingx.color.ColorUtil;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.renderer.ComponentProvider;
import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.table.ColumnFactory;
import org.jdesktop.swingx.table.TableColumnExt;
import org.openide.util.Lookup;

/**
 *
 * @author dafe
 */
public final class Utilities {

    private static final INameEncoder nameEncoder = Lookup.getDefault().lookup(IImporterServices.class).getNameEncoder();
    private static final IMimeTypeDetector mimeDetector = Lookup.getDefault().lookup(IImporterServices.class).getMimeDetector();

    public static void assureSelected(JTable table, Point point) {
        int popupRow = table.rowAtPoint(point);
        if (popupRow != -1 && !table.getSelectionModel().isSelectedIndex(popupRow)) {
            table.getSelectionModel().addSelectionInterval(popupRow, popupRow);
        }
    }

    public static void addAction2Popup(JPopupMenu popup, Action action) {
        if (action.isEnabled()) {
            popup.add(action);
        }
    }
    
    public static void addMusicServices2Popup(JPopupMenu popup, Lookup songsLkp) {
        Collection<? extends IMusicService> musicServices = MusicServiceRegistry.getInstance().getServices();
        for (IMusicService ms : musicServices) {
            JMenu serviceSubMenu = new JMenu(ms.getDisplayName());
            List<Action> serviceActions = createURLActions(ms, songsLkp);
            for (Action action : serviceActions) {
                serviceSubMenu.add(action);
            }
            popup.add(serviceSubMenu);
        }
    }
    
    public static List<Action> createURLActions(IMusicService ms, Lookup songsLkp) {
        List<Action> result = new ArrayList<Action>();
        
        List<IURIActionDescription<ISong>> songActionDescs = ms.getSongURLActionDescriptions();
        for (IURIActionDescription<ISong> actionDesc : songActionDescs) {
            result.add(new Go2URIAction(actionDesc, songsLkp));
        }
        
        List<IURIActionDescription<IAuthor>> authorActionDescs = ms.getAuthorURLActionDescriptions();
        for (IURIActionDescription<IAuthor> actionDesc : authorActionDescs) {
            result.add(new Go2URIAction(songsLkp, actionDesc));
        }
        
        return result;
    }

    public static boolean isSongMatch(ISong song, String searchText) {
        searchText = nameEncoder.encodeName(searchText);
        String songName = song.getName();
        if (songName != null) {
            songName = nameEncoder.encodeName(songName);
        }
        IAuthor author = song.getAuthor();
        String authorName = null;
        if (author != null) {
            authorName = author.getName();
            if (authorName != null) {
                authorName = nameEncoder.encodeName(authorName);
            }
        }
        return (songName != null && songName.indexOf(searchText) != -1)
                || (authorName != null && authorName.indexOf(searchText) != -1);

    }

    public static String getConcertString(IConcert concert) {
        DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
        return dateFormat.format(concert.getDate()) + " - " + concert.getLocation();
    }

    public static ISongResource getTextWithChords(ISong song) {
        return getPreferredResourceOfType(song, IMimeTypeDetector.ContentType.TEXT_WITH_CHORDS);
    }

    public static ISongResource getAudio(ISong song) {
        return getPreferredResourceOfType(song, IMimeTypeDetector.ContentType.AUDIO);
    }
    
    public static ISongResource getVideo(ISong song) {
        return getPreferredResourceOfType(song, IMimeTypeDetector.ContentType.VIDEO);
    }
    
    public static ISongResource getTablature(ISong song) {
        return getPreferredResourceOfType(song, IMimeTypeDetector.ContentType.TABLATURES_NOTES);
    }
    
    public static ISongResource getPreferredResourceOfType(ISong song, IMimeTypeDetector.ContentType ct) {
        ISongResource lastModifRes = null;
        for (ISongResource songRes : song.getResources()) {
            if (isResourceOfType(songRes, ct)) {
                if (songRes.isPreferred()) {
                    return songRes;
                }
                if (lastModifRes == null || lastModifRes.getLastModified() < songRes.getLastModified()) {
                    lastModifRes = songRes;
                }
            }
        }
        return lastModifRes;
    }

    public static Icon getArrowIcon(int arrowKey) {
        String imgPath = null;
        switch (arrowKey) {
            case KeyEvent.VK_UP:
                imgPath = "up.gif";
                break;
            case KeyEvent.VK_DOWN:
                imgPath = "down.gif";
                break;
            case KeyEvent.VK_LEFT:
                imgPath = "left.gif";
                break;
            case KeyEvent.VK_RIGHT:
                imgPath = "right.gif";
                break;
            default:
                throw new IllegalArgumentException("Not valid arrow key code");
        }
        return new ImageIcon(Utilities.class.getResource(imgPath));
    }

    public static Icon getSeverityIcon(IImportIssue.Severity severity) {
        String imgPath = null;
        switch (severity) {
            case DEFECT:
                imgPath = "error.gif";
                break;
            case WARNING:
                imgPath = "warning.gif";
                break;
            case INFO:
                imgPath = "information.gif";
                break;
            default:
                throw new IllegalArgumentException("Not valid severity");
        }
        return new ImageIcon(Utilities.class.getResource(imgPath));
    }


    public static JXPanel createCollapsibleHandler(int arrowKey, String tooltip, final JXCollapsiblePane collapsiblePane) {
        final JButton button = new JButton(Utilities.getArrowIcon(arrowKey));
        if (tooltip != null) {
            button.setToolTipText(tooltip);
        }
        button.putClientProperty("ArrowKeyCode", arrowKey);
        JXPanel buttonPanel = new JXPanel(new BorderLayout());
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
        buttonPanel.setOpaque(false);
        buttonPanel.add(button, BorderLayout.CENTER);
        button.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                int oppositeKey = getOppositeArrow((Integer) button.getClientProperty("ArrowKeyCode"));
                button.putClientProperty("ArrowKeyCode", oppositeKey);
                button.setIcon(getArrowIcon(oppositeKey));
                collapsiblePane.setCollapsed(!collapsiblePane.isCollapsed());
            }
            private Map<Integer, Integer> oppositeMap = new HashMap<Integer, Integer>() {

                {
                    put(KeyEvent.VK_UP, KeyEvent.VK_DOWN);
                    put(KeyEvent.VK_DOWN, KeyEvent.VK_UP);
                    put(KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT);
                    put(KeyEvent.VK_RIGHT, KeyEvent.VK_LEFT);
                }
            };

            private int getOppositeArrow(int arrowKey) {
                return oppositeMap.get(arrowKey);
            }
        });

        return buttonPanel;
    }

    public static JXMultiSplitPane.DividerPainter createDividerPainter (final JXMultiSplitPane splitPane) {
        return new JXMultiSplitPane.DividerPainter() {
            @Override
            protected void doPaint(Graphics2D gd, Divider divider, int width, int height) {
                Rectangle rect = divider.isVertical()
                        ? new Rectangle(width / 3, 0, width / 3, height)
                        : new Rectangle(0, height / 3, width, height / 3);
                Color bk = splitPane.getBackground();
                Color fg = splitPane.getForeground();
                float emphasis = divider.equals(splitPane.activeDivider()) ? 0.3f : 0.1f;
                Color dividerC = ColorUtil.interpolate(fg, bk, emphasis);
                gd.setPaint(new GradientPaint(rect.x, rect.y, bk,
                        (float)rect.getCenterX(), (float) rect.getCenterY(), dividerC,
                        true));
                gd.fill(rect);
            }
        };
    }

    public static boolean isTextType(ISongResource songRes) {
        return isResourceOfType(songRes, IMimeTypeDetector.ContentType.TEXT_WITH_CHORDS);
    }

    public static boolean isResourceOfType(ISongResource songRes, IMimeTypeDetector.ContentType ct) {
        return ct.equals(mimeDetector.getContentType(songRes.getMimeType()));
    }
    
    public static String getStringNumber(int number, int charCount) {
        String strNumber = String.valueOf(number);
        if (charCount < strNumber.length()) {
            throw new IllegalArgumentException("charCount value must not be less then character count in number");
        }
        
        int nullCount = charCount - strNumber.length();
        char[] nulls = new char[nullCount];
        for (int i = 0; i < nullCount; i++) {
            nulls[i] = '0';
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append(nulls);
        sb.append(strNumber);
        
        return sb.toString();
    }

    public static class CustomColumnFactory extends ColumnFactory {

        private Map<Object, ColumnDescription> columnDescriptions =
                new HashMap<Object, ColumnDescription>();

        public static class ColumnDescription {
            public boolean isExcluded;
            public boolean isHidden;
            public ComponentProvider<?> componentProvider;
            public Highlighter highlighter;
            public PrototypeDescription prototypeDescription;
            public Comparator<?> comparator;
            public String title;
            public String tooltip;
            public TableCellEditor cellEditor;
        }

        public static class PrototypeDescription {
            private Object value;
            private float minWidthRatio;
            private float maxWidthRatio;

            public PrototypeDescription(Object value, float minWidthRatio, float maxWidthRatio) {
                this.value = value;
                this.minWidthRatio = minWidthRatio;
                this.maxWidthRatio = maxWidthRatio;
            }
        }

        @Override
        public void configureTableColumn(TableModel model, TableColumnExt columnExt) {
            super.configureTableColumn(model, columnExt);
            columnExt.setIdentifier(model.getColumnName(columnExt.getModelIndex()));
            ColumnDescription colDesc = columnDescriptions.get(columnExt.getIdentifier());
            if (colDesc == null) {
                return;
            }

            if (colDesc.title != null) {
                columnExt.setTitle(colDesc.title);
            }
            if (colDesc.tooltip != null) {
                columnExt.setToolTipText(colDesc.tooltip);
            }
            if (colDesc.componentProvider != null) {
                columnExt.setCellRenderer(new DefaultTableRenderer(colDesc.componentProvider));
            }
            if (colDesc.highlighter != null) {
                columnExt.setHighlighters(colDesc.highlighter);
            }
            if (colDesc.comparator != null) {
                columnExt.setComparator(colDesc.comparator);
            }
            if (colDesc.prototypeDescription != null) {
                columnExt.setPrototypeValue(colDesc.prototypeDescription.value);
            }
            if (colDesc.cellEditor != null) {
                columnExt.setCellEditor(colDesc.cellEditor);
            }
            if (colDesc.isHidden) {
                columnExt.setVisible(false);
            }
        }

        @Override
        public void configureColumnWidths(JXTable table, TableColumnExt columnExt) {
            super.configureColumnWidths(table, columnExt);
            ColumnDescription colDesc = columnDescriptions.get(columnExt.getIdentifier());
            PrototypeDescription prototypeDescription = colDesc != null ? colDesc.prototypeDescription : null;
            if (prototypeDescription != null) {
                if (prototypeDescription.minWidthRatio != 0) {
                    columnExt.setMinWidth(Math.round(
                            columnExt.getPreferredWidth() * prototypeDescription.minWidthRatio));
                }
                if (prototypeDescription.maxWidthRatio != 0) {
                    columnExt.setMaxWidth(Math.round(
                            columnExt.getPreferredWidth() * prototypeDescription.maxWidthRatio));
                }
            }
        }

        /**
         * {@inheritDoc} <p>
         * Overridden to check if the wants to limit the count of rows when
         * measuring.
         */
        @Override
        protected int getRowCount(JXTable table) {
            if (table.getClientProperty("ColumnFactory.maxRowCount") instanceof Integer) {
                return Math.min(table.getRowCount(),
                        (Integer) table.getClientProperty("ColumnFactory.maxRowCount"));
            }
            return super.getRowCount(table);
        }

        public void setColumnDescription(Object id, ColumnDescription colDesc) {
            columnDescriptions.put(id, colDesc);
        }

    }

    public static boolean isServerMode () {
        return Boolean.getBoolean("partyband.server");
    }
    
    public static List<ISongResource> getSelectedResources(JXTable songsTable, AbstractSongTableModel songsModel) {
        List<ISongResource> result = new ArrayList<ISongResource>();
        List<ISong> selSongs = getSelectedSongs(songsTable, songsModel);
        for (ISong song : selSongs) {
            result.addAll(song.getResources());
        }
        return result;
    }

    public static List<ISong> getSelectedSongs(JXTable songsTable, AbstractSongTableModel songsModel) {
        List<ISong> result = new ArrayList<ISong>();
        int[] selRows = songsTable.getSelectedRows();
        for (int i = 0; i < selRows.length; i++) {
            result.add(songsModel.getSongAtRow(songsTable.convertRowIndexToModel(selRows[i])));
        }
        return result;
    }
    

}
